#ifndef RP2RENDERER_H
#define RP2RENDERER_H

#include "Rp2System.h"
#include <GL/glew.h>
#include <GL/GL.h>

// The NVIDIA Cg support
#include <Cg/cg.h>
#include <Cg/cgGL.h>

#include "Rp2GlobalState.h"
#include "Rp2AlphaState.h"
#include "Rp2WireframeState.h"
#include "Rp2ZBufferState.h"
#include "Rp2MaterialState.h"
#include "Rp2CullState.h"
#include "Rp2PolygonOffsetState.h"
#include "Rp2StencilState.h"

#include "Rp2OpenGLBitmapFont.h"
#include "Rp2ColorRGB.h"
#include "Rp2ColorRGBA.h"
#include "Rp2Matrix4.h"
#include "Rp2SamplerInformation.h"
#include "Rp2RendererConstant.h"
#include "Rp2Tga.h"

namespace Rp2
{

class Attributes;
class Bindable;
class Camera;
class Effect;
class Geometry;
class IndexBuffer;
class Light;
class PixelProgram;
class ResourceIdentifier;
class ShaderEffect;
class Spatial;
class Tga;
class Texture;
class VertexBuffer;
class VertexProgram;
class VisibleObject;
class VisibleSet;

class Renderer
{
public:
	Renderer(int iWinWidth, int iWinHeight);
	~Renderer();

	//bool SetupRC();
	virtual void ClearBuffers() = 0;
	void DisplayBackBuffer();
	void Resize(int iWidth, int iHeight);	
	void Release();

    // Make this renderer context the active one.
    virtual void Activate ();
    virtual bool IsActive () const;

    // Renderer-specific information for loading shader programs.
    //virtual const char* GetExtension ();
    //virtual char GetCommentCharacter ();
    static int GetMaxLights ();
    static int GetMaxColors ();
    static int GetMaxTCoords ();
    static int GetMaxVShaderImages ();
    static int GetMaxPShaderImages ();
    static int GetMaxStencilIndices ();
    static int GetMaxUserClipPlanes ();
    static float GetMaxAnisotropy ();

	// set transform
	void SetWorldTransformation();
	void RestoreWorldTransformation();

	// world matrix for geometry
	Matrix4f GetWorldMatrix() const;

	// set\get render states
	void SetGlobalState(GlobalStatePtr aspkGlobalState[]);
	void RestoreGlobalState(GlobalStatePtr aspkGlobalState[]);
	virtual void SetAlphaState(AlphaState* pkAlphaState);
	virtual void SetWireframeState(WireframeState* pkWireframeState);
	virtual void SetZBufferState(ZBufferState* pkZBufferState);
	virtual void SetMaterialState(MaterialState* pkMaterialState);
	virtual void SetCullState(CullState* pkCullState);
	virtual void SetPolygonOffsetState(PolygonOffsetState* pkOffsetState);
	virtual void SetStencilState(StencilState* pkStencilState);
	AlphaState* GetAlphaState();
	CullState* GetCullState ();
    MaterialState* GetMaterialState ();
    PolygonOffsetState* GetPolygonOffsetState ();
    StencilState* GetStencilState ();
    WireframeState* GetWireframeState ();
    ZBufferState* GetZBufferState ();	

    // Function pointer types for binding and unbinding resources.
    typedef void (Renderer::*ReleaseFunction)(Bindable*);

    // Resource loading and releasing.
    void LoadAllResources (Spatial* pkScene);
    void ReleaseAllResources (Spatial* pkScene);
    void LoadResources (Geometry* pkGeometry);
    void ReleaseResources (Geometry* pkGeometry);
    void LoadResources (Effect* pkEffect);
    void ReleaseResources (Effect* pkEffect);
    void LoadVProgram (VertexProgram* pkVProgram);
    void ReleaseVProgram (Bindable* pkVProgram);
    void LoadPProgram (PixelProgram* pkPProgram);
    void ReleasePProgram (Bindable* pkPProgram);
    void LoadTexture (Texture* pkTexture);
    void ReleaseTexture (Bindable* pkTexture);
    void LoadVBuffer (const Attributes& rkIAttr, const Attributes& rkOAttr,
        VertexBuffer* pkVBuffer);
    void ReleaseVBuffer (Bindable* pkVBuffer);
    void LoadIBuffer (IndexBuffer* pkIBuffer);
    void ReleaseIBuffer (Bindable* pkIBuffer);

    // Resource enabling and disabling.
    enum  // ConstantType
    {
        CT_RENDERER,
        CT_NUMERICAL,
        CT_USER
    };
    virtual void SetVProgramConstant (int eCType, int iBaseRegister,
        int iRegisterQuantity, float* afData);
    virtual void SetPProgramConstant (int eCType, int iBaseRegister,
        int iRegisterQuantity, float* afData);


    void EnableVProgram (VertexProgram* pkVProgram);
    void DisableVProgram (VertexProgram* pkVProgram);
    void EnablePProgram (PixelProgram* pkPProgram);
    void DisablePProgram (PixelProgram* pkPProgram);
    void EnableTexture (Texture* pkTexture);
    void DisableTexture (Texture* pkTexture);
    ResourceIdentifier* EnableVBuffer (const Attributes& rkIAttr,
        const Attributes& rkOAttr);
    void DisableVBuffer (ResourceIdentifier* pkID);
    void EnableIBuffer ();
    void DisableIBuffer ();

    // For use by effects with lights.
    void SetLight (int i, Light* pkLight);
    Light* GetLight (int i);

	// Camera
	void SetCamera(Camera* pkCamera);
	void OnFrustumChange();
	void OnFrameChange();
	void OnViewportChange();
	Matrix4f GetViewMatrix() const;
	Matrix4f GetProjectionMatrix() const;
	Matrix4f GetViewProjectionMatrix() const;
	Matrix3f GetNormalMatrix() const;	// rotate normal from world space to eye space.

	// Orthographic matrix
	void OnCreateOrthoMatrix();
	Matrix4f GetOrthoMatrix() const;

	// Function pointer types for binding and unbinding resources.
	typedef void (Renderer::*ReleaseFunction)(Bindable*);

    // Text drawing.
	virtual void Draw(int iX, int iY, const ColorRGBA& rkColor,
		const char* acText);

	// Draw functions
	void DrawScene(VisibleSet& pkScene);
	void Draw(Geometry* pkGeometry);
	virtual void DrawElements() = 0;
	void ApplyEffect(ShaderEffect* pkEffect, bool& rbPrimaryEffect);
	
	bool BeginScene();
	void EndScene();

protected:
    // Resource limits.  The values are set by the Renderer-derived objects.
    static int ms_iMaxLights;
    static int ms_iMaxColors;
    static int ms_iMaxTCoords;
    static int ms_iMaxVShaderImages;
    static int ms_iMaxPShaderImages;
    static int ms_iMaxStencilIndices;
    static int ms_iMaxUserClipPlanes;
    static float ms_fMaxAnisotropy;

    // Active samplers in shader programs, at most m_iMaxVShaderImages of them
    // for vertex programs and at most m_iMaxPShaderImages of them for pixel
    // programs.
    int m_iNumActiveSamplers;
    int m_iCurrentSampler;
    SamplerInformation** m_apkActiveSamplers;

	// implememt by derived class
    // Resource loading and releasing (to/from video memory).
	virtual void OnLoadVProgram (ResourceIdentifier*& rpkID, VertexProgram* pkVProgram) = 0;
    virtual void OnReleaseVProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnLoadPProgram (ResourceIdentifier*& rpkID, PixelProgram* pkPProgram) = 0;
    virtual void OnReleasePProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnLoadTexture (ResourceIdentifier*& rpkID, Texture* pkTexture)  = 0;
    virtual void OnReleaseTexture (ResourceIdentifier* pkID) = 0;
    virtual void OnLoadVBuffer (ResourceIdentifier*& rkpID, const Attributes& rkIAttr,
		const Attributes& rkOAttr, VertexBuffer* pkVBuffer) = 0;
    virtual void OnReleaseVBuffer (ResourceIdentifier* pkID) = 0;
    virtual void OnLoadIBuffer (ResourceIdentifier*& rpkID, IndexBuffer* pkIBuffer) = 0;
    virtual void OnReleaseIBuffer (ResourceIdentifier* pkID) = 0;

	// Enable / disable resource
    virtual void OnEnableVProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnDisableVProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnEnablePProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnDisablePProgram (ResourceIdentifier* pkID) = 0;
    virtual void OnEnableTexture (ResourceIdentifier* pkID) = 0;
    virtual void OnDisableTexture (ResourceIdentifier* pkID) = 0;
    virtual void OnEnableVBuffer (ResourceIdentifier* pkID) = 0;
    virtual void OnDisableVBuffer (ResourceIdentifier* pkID) = 0;
    virtual void OnEnableIBuffer (ResourceIdentifier* pkID) = 0;
    virtual void OnDisableIBuffer (ResourceIdentifier* pkID) = 0;

    // The operations are
    //   0 = matrix
    //   1 = transpose of matrix
    //   2 = inverse of matrix
    //   3 = inverse-transpose of matrix
    void GetTransform (const Matrix4f& rkMat, int iOperation, float* afData);
    void SetConstantWMatrix (int iOperation, float* afData);
    void SetConstantVMatrix (int iOperation, float* afData);
    void SetConstantPMatrix (int iOperation, float* afData);
    void SetConstantWVMatrix (int iOperation, float* afData);
    void SetConstantVPMatrix (int iOperation, float* afData);
    void SetConstantWVPMatrix (int iOperation, float* afData);

    // These functions do not use the option parameter, but the parameter is
    // included to allow for a class-static array of function pointers to
    // handle all shader constants.
    void SetConstantMaterialEmissive (int, float* afData);
    void SetConstantMaterialAmbient (int, float* afData);
    void SetConstantMaterialDiffuse (int, float* afData);
    void SetConstantMaterialSpecular (int, float* afData);
    void SetConstantCameraModelPosition (int, float* afData);
    void SetConstantCameraModelDirection (int, float* afData);
    void SetConstantCameraModelUp (int, float* afData);
    void SetConstantCameraModelRight (int, float* afData);
    void SetConstantCameraWorldPosition (int, float* afData);
    void SetConstantCameraWorldDirection (int, float* afData);
    void SetConstantCameraWorldUp (int, float* afData);
    void SetConstantCameraWorldRight (int, float* afData);
    void SetConstantProjectorModelPosition (int, float* afData);
    void SetConstantProjectorModelDirection (int, float* afData);
    void SetConstantProjectorModelUp (int, float* afData);
    void SetConstantProjectorModelRight (int, float* afData);
    void SetConstantProjectorWorldPosition (int, float* afData);
    void SetConstantProjectorWorldDirection (int, float* afData);
    void SetConstantProjectorWorldUp (int, float* afData);
    void SetConstantProjectorWorldRight (int, float* afData);
    void SetConstantProjectorMatrix (int, float* afData);

    // These functions set the light state.  The index iLight is between 0
    // and 7 (eight lights are currently supported).
    void SetConstantLightModelPosition (int iLight, float* afData);
    void SetConstantLightModelDirection (int iLight, float* afData);
    void SetConstantLightWorldPosition (int iLight, float* afData);
    void SetConstantLightWorldDirection (int iLight, float* afData);
    void SetConstantLightAmbient (int iLight, float* afData);
    void SetConstantLightDiffuse (int iLight, float* afData);
    void SetConstantLightSpecular (int iLight, float* afData);
    void SetConstantLightSpotCutoff (int iLight, float* afData);
    void SetConstantLightAttenuation (int iLight, float* afData);

    enum { SC_QUANTITY = 36 };
    typedef void (Renderer::*SetConstantFunction)(int,float*);
    static SetConstantFunction ms_aoSCFunction[SC_QUANTITY];
    void SetRendererConstant (RendererConstant::Type eRCType, float* afData);

	// Text drawing
	void DrawCharacter(const BitmapFont& rkFont, char cChar);

	// window params
	HWND m_hWnd;
	HDC  m_hDC;
	HGLRC m_hGLrc;
	int m_iXPosition, m_iYPosition, m_iWidth, m_iHeight;

	// shader effects
	ShaderEffect* m_pkShaderEffect;


	// uniform
	GLuint uiLocMvpMatrix;
	GLuint uiLocMvMatrix;
	GLuint uiLocVMatrix;
	GLuint uiLocLightViewMatrix;
	GLuint uiLocWorldLightPos;
	GLuint uiLocAmbientColor;
	GLuint uiLocDiffuseColor;
	GLuint uiLocSpecularColor;
	GLuint uiLocTexture;
	GLuint uiLocDissolveFactor;
	
	// vertex buffer
	GLuint auiBufferObject[4];
	GLuint uiTexID;

	// current geometry for drawing
	Geometry* m_pkGeometry;

	// camera
	Camera* m_pkCamera;
	Matrix4f m_kOrthoMatrix;

    // The projector for various effects such as projected textures and
    // shadow maps.
    Camera* m_pkProjector;

    // Transformations used in the geometric pipeline.  These matrices are
    // stored to support 1x4 row vectors times 4x4 matrices.
    Matrix4f m_kWorldMatrix, m_kSaveWorldMatrix;
    Matrix4f m_kViewMatrix, m_kSaveViewMatrix;
    Matrix4f m_kProjectionMatrix, m_kSaveProjectionMatrix;

    // Post-projection matrix.  For example, you can use this to reflect the
    // rendering.
    Matrix4f m_kPostProjectionMatrix;
    bool m_bPostProjectionIsIdentity;	

	// current render states
	GlobalStatePtr m_aspkState[GlobalState::MAX_STATE_TYPE];

	// Light storage for lookup by the shader-constant-setting functions.
    // The Renderer-derived classes must allocate this array during
    // construction, creating m_iMaxLights elements.  The Renderer class
    // deallocates the array during destruction.
    ObjectPtr* m_aspkLight;

	// support for mirror effects (default : false)
	bool m_bReverseCullFace;

    static GLenum ms_aeAlphaSrcBlend[AlphaState::SBF_QUANTITY];
    static GLenum ms_aeAlphaDstBlend[AlphaState::DBF_QUANTITY];
    static GLenum ms_aeAlphaTest[AlphaState::TF_QUANTITY];	
    static GLenum ms_aeFrontFace[CullState::FT_QUANTITY];
    static GLenum ms_aeCullFace[CullState::CT_QUANTITY];
    static GLenum ms_aeStencilCompare[StencilState::CF_QUANTITY];
    static GLenum ms_aeStencilOperation[StencilState::OT_QUANTITY];
    static GLenum ms_aeZBufferCompare[ZBufferState::CF_QUANTITY];
};

#include "Rp2Renderer.inl"
}

#endif